﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace RecommenderSystem.misc
{
    class DecisionTree
    {
        private ItemNode<string> root { get; set; }

        public DecisionTree(ICollection<User> lUsers, string itemId)
        {
            ICollection<string> lItems = new List<string>();
            foreach (User user in lUsers)
            {
                if (user.getItemRate(itemId) > -1)
                {
                    foreach (string item in user.getRatedItemsIds())
                    {
                        lItems.Add(item);
                    }
                }
            }

            root = buildTree(lUsers.Where(x => x.getItemRate(itemId) > -1).ToList(), lItems.Where(x => !x.Equals(itemId)).Distinct().ToList());
        }

        public double getPrediction(User user)
        {
            return getPrediction(user, root);
        }

        private double getPrediction(User user, ItemNode<String> node)
        {
            if (node.isLeaf)
            {
                return node.answer;
            }

            string nodeItem = node.getAttribute();

            if (user.getItemRate(nodeItem) > -1)
            {
                return getPrediction(user, node.getChild(true));
            }
            else
            {
                return getPrediction(user, node.getChild(false));
            }

        }


        private ItemNode<string> buildTree(ICollection<User> lUsers, ICollection<string> lItems)
        {

            int usersCount = lUsers.Count();

            if (usersCount < Config.NODE_SPLIT_THRESHOLD)
            {
                ItemNode<string> nod = new ItemNode<String>("");
                nod.isLeaf = true;
                nod.answer = usersCount != 0 ? lUsers.Average(x => x.getAverageRating()) : 0;
                return nod;
            }


            string itemToSplitOn = computeMaxScore(lUsers, lItems);

            var posUsers = lUsers.Where(x => x.getItemRate(itemToSplitOn) > -1).ToList();
            var negUsers = lUsers.Where(x => x.getItemRate(itemToSplitOn) == -1).ToList();

            ItemNode<string> node = new ItemNode<String>(itemToSplitOn);

            if (posUsers.Count() == 0 || negUsers.Count() == 0)
            {
                node.isLeaf = true;
                node.answer = posUsers.Count() != 0 ?   posUsers.Average(x => x.getAverageRating()) :
                                                        negUsers.Average(x=> x.getAverageRating());
                return node;
            }
            node.isLeaf = false;
            var newItemsList = lItems.Where(x => !x.Equals(itemToSplitOn)).ToList();
            node.addChild(true, buildTree(posUsers, newItemsList));
            node.addChild(false, buildTree(negUsers, newItemsList));

            return node;
        }

        private string computeMaxScore(IEnumerable<User> users, ICollection<string> lItems)
        {
            Dictionary<string, int> itemScores = new Dictionary<string, int>();

            foreach (string itemId in lItems)
            {
                foreach (User user in users)
                {
                    if (user.getItemRate(itemId) > -1)
                    {
                        if (itemScores.ContainsKey(itemId))
                        {
                            itemScores[itemId]++;
                        }
                        else
                        {
                            itemScores.Add(itemId, 1);
                        }
                    }
                }
            }

            int usersCount = users.Count();
            int maxScore = 0;
            string itemWithMaxScore = " ";

            foreach (string itemId in itemScores.Keys)
            {
                int usersWhoUsed = itemScores[itemId];
                int usersWhoNotUsed = usersCount - usersWhoUsed;
                int score = Math.Abs(usersWhoUsed - usersWhoNotUsed);

                if (score > maxScore)
                {
                    maxScore = score;
                    itemWithMaxScore = itemId;
                }
            }


            return itemWithMaxScore;
        }
    }
}
